import { CircularProgress, TextField } from '@mui/material'
import Autocomplete, {
	type AutocompleteProps
} from '@mui/material/Autocomplete'
import type { UseMutateFunction } from '@tanstack/react-query'
import { useCallback, useEffect, useState } from 'react'
import { type Control, useController } from 'react-hook-form'

import { useDebounce } from '@/utils/throttle-debounce.util'

interface OptionType {
	id: number | null
	name: string
}

const noLinkValue = {
	id: null,
	name: 'Не связан'
}

interface AutocompleteControlledProps
	extends Partial<
		Omit<
			AutocompleteProps<OptionType, boolean, boolean, boolean>,
			'renderInput'
		>
	> {
	control: Control<any>
	callback: {
		mutate: UseMutateFunction<any, Error, string, unknown>
		options: OptionType[]
		isPending: boolean
		enabled: boolean
	}
	name: string
	label: string
	defVal: { exists: boolean; value?: OptionType }
}

export const AutocompleteControlled = ({
	control,
	callback,
	name,
	label,
	defVal,
	...props
}: AutocompleteControlledProps) => {
	const { field } = useController({
		control,
		name: name
	})
	const [options, setOptions] = useState<OptionType[]>([])
	const [value, setValue] = useState<{ id: number | null; name: string }>(
		noLinkValue
	)
	const [inputValue, setInputValue] = useState<string>('')

	// useEffect for handling changes in defVal.value
	useEffect(() => {
		if (field.value !== value.id)
			setValue(
				defVal.value && field.value !== null ? defVal.value : noLinkValue
			)
	}, [defVal.value, field.value, value.id])

	// useEffect for handling changes in callback.options
	useEffect(() => {
		if (
			!callback.options.some(option => option.id === defVal.value?.id) &&
			defVal.value
		)
			setOptions([noLinkValue, defVal.value, ...callback.options])
		else setOptions([noLinkValue, ...callback.options])
	}, [callback.options, defVal.value])

	const [open, setOpen] = useState(false)
	useEffect(() => {
		if (open && options.length <= 2 && callback.enabled) callback.mutate('')
	}, [open])

	const handleInputChange = useCallback(
		(newValue: string) => setInputValue(newValue),
		[]
	)

	const handleInputChangeEnd = useDebounce(
		(newValue: string) => newValue.length >= 2 && callback.mutate(newValue),
		700
	)

	return (
		<Autocomplete
			{...props}
			value={value}
			onChange={(_, newValue) => {
				if (
					typeof newValue === 'string' ||
					Array.isArray(newValue) ||
					!newValue
				)
					return
				field.onChange(newValue.id)
				setValue(newValue)
			}}
			inputValue={inputValue}
			onInputChange={(_: any, newValue: string, reason) => {
				handleInputChange(newValue)
				if (reason === 'input') handleInputChangeEnd(newValue)
			}}
			options={options}
			isOptionEqualToValue={(option, value) => option.id === value.id}
			getOptionLabel={option =>
				typeof option === 'string' ? option : option.name
			}
			loading={callback.isPending}
			open={open}
			onOpen={() => setOpen(true)}
			onClose={() => setOpen(false)}
			renderInput={params => (
				<TextField
					{...params}
					label={label}
					InputProps={{
						...params.InputProps,
						endAdornment: (
							<>
								{callback.isPending ? (
									<CircularProgress
										color='inherit'
										size={20}
									/>
								) : null}
								{params.InputProps.endAdornment}
							</>
						)
					}}
				/>
			)}
			fullWidth
		/>
	)
}
